package com.devunion.bogdan.model.project;

import com.devunion.bogdan.model.event.AnnotationListener;
import com.devunion.bogdan.model.event.AnnotationEvent;

import javax.swing.event.EventListenerList;
import javax.persistence.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.awt.*;
import java.io.Serializable;
import java.io.IOException;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.hibernate.annotations.IndexColumn;
import org.apache.log4j.Logger;

/**
 * @author Viktar Liaskovich
 */
@Entity
public class Page implements Serializable {
    public static final String IMAGE_FORMAT = "png";

    private static Logger log = Logger.getLogger(Page.class);

    @Id
    @GeneratedValue
    @Column(name = "pageId")
    private long pageId;

    @Column(name = "pageNumber")
    private Integer pageNumber;

    @Column(name = "relativePath")
    private String relativePath;

    private transient Image imageObject;

    @OneToMany(cascade = CascadeType.ALL)
    @Cascade({org.hibernate.annotations.CascadeType.ALL})
    @LazyCollection(LazyCollectionOption.FALSE)
    @IndexColumn(name="IND_COL")
    private List<Annotation> annotations = new ArrayList<Annotation>();

    protected transient EventListenerList listenerList = new EventListenerList();

    public Page() {
    }

    public Page(Integer pageNumber) {
        this.pageNumber = pageNumber;
    }

    public long getPageId() {
        return pageId;
    }

    public void setPageId(long pageId) {
        this.pageId = pageId;
    }

    public Integer getPageNumber() {
        return pageNumber;
    }

    public String getRelativePath() {
        return relativePath;
    }

    public void setRelativePath(String relativePath) {
        this.relativePath = relativePath;
    }

    public Image getImageObject() {
        if (imageObject == null) {
            try {
                imageObject = ImageHelper.getInstance().loadImage(relativePath);
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return imageObject;
    }

    public void setImageObject(Image image) {
        this.imageObject = image;

        try {
            this.relativePath = ImageHelper.getInstance().saveImage(imageObject, pageNumber);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    public String toString() {
        return "Page " + pageNumber;
    }

    public void addAnnotation(Annotation annotation) {
        if (annotation == null) {
            throw new IllegalArgumentException("Annotation can't be null");
        }

        annotations.add(annotation);
        fireAnnotationAddedEvent(new AnnotationEvent(this, annotation));
    }

    public void removeAnnotation(Annotation annotation) {
        annotations.remove(annotation);
        fireAnnotationRemovedEvent(new AnnotationEvent(this, annotation));
    }

    public List<Annotation> getAnnotations() {
        return Collections.unmodifiableList(annotations);
    }

    public void addAnnotationListener(AnnotationListener listener) {
        listenerList.add(AnnotationListener.class, listener);
    }

    public void removeAnnotationListener(AnnotationListener listener) {
        listenerList.remove(AnnotationListener.class, listener);
    }

    public AnnotationListener[] getAnnotationListeners() {
        return listenerList.getListeners(AnnotationListener.class);
    }

    void fireAnnotationAddedEvent(AnnotationEvent e) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();

        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 1; i >= 0; i--) {
            if (listeners[i] == AnnotationListener.class) {
                ((AnnotationListener) listeners[i + 1]).annotationAdded(e);
            }
        }
    }

    void fireAnnotationRemovedEvent(AnnotationEvent e) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();

        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 1; i >= 0; i--) {
            if (listeners[i] == AnnotationListener.class) {
                ((AnnotationListener) listeners[i + 1]).annotationRemoved(e);
            }
        }
    }

    public void resendEvents() {
        for (Annotation annotation : annotations) {
            fireAnnotationAddedEvent(new AnnotationEvent(this, annotation));
        }
    }
}
